﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

using RexToy.ExpressionLanguage;
using RexToy.UI.WinForm.Pmlx;
using RexToy.UI.WinForm.ViewModel;

namespace RexToy.UI.WinForm.Controls
{
    public enum DataExchangeType
    {
        ID = 0,
        Entity = 1
    }

    public class DataComboBox : ComboBoxEx, ITemplateItem
    {
        private ExpressionLanguageEngine _engine;
        public DataComboBox()
        {
            _engine = ExpressionLanguageEngine.CreateEngine(UIELEngineConfig.Config);
        }

        public DataExchangeType DataExchangeType { get; set; }

        public void Bind<T>(IEnumerable<T> items)
        {
            this.BindType<T>();

            this.BeginUpdate();
            foreach (T item in items)
            {
                _engine.Assign(ControlConstant.EVAL_CTX_ITEM, item);

                ComboBoxItem cbi = new ComboBoxItem();
                IReflector r = Reflector.Bind(item);
                cbi.ID = string.IsNullOrEmpty(_key) ? item : r.GetPropertyValue(_key);
                cbi.DisplayName = _engine.Eval(_displayExpr);
                cbi.Item = item;

                this.Items.Add(cbi);
            }
            this.EndUpdate();
        }

        private string _key;
        private string _displayExpr;
        public void ParseTemplate(PmlxNode template)
        {
            _key = template.NavigateToSingle(ControlConstant.KEY).Value;
            _displayExpr = template.NavigateToSingle(ControlConstant.DISPLAY_EXPRESSION).Value;
            if (_displayExpr.BracketedBy(StringPair.Template_Bracket))
            {
                _displayExpr = _displayExpr.UnBracketing(StringPair.Template_Bracket);
            }
            else if (_displayExpr.BracketedBy(StringPair.Delayed_Template_Bracket))
            {
                _displayExpr = _displayExpr.UnBracketing(StringPair.Delayed_Template_Bracket);
            }
            else
            {
                ThrowHelper.ThrowMustBindExpression();
            }
        }

        public override void UpdateView(ViewModelBase model)
        {
            IReflector r = Reflector.Bind(model);
            object val = r.GetPropertyValue(BindPropertyName);

            switch (DataExchangeType)
            {
                case DataExchangeType.Entity:
                    this.InternalSetSelectedItem(val);
                    break;

                case DataExchangeType.ID:
                    this.InternalSetSelectedKey(val);
                    break;
            }
        }

        public override void UpdateModel(ViewModelBase model)
        {
            IReflector r = Reflector.Bind(model);
            object val = this.InternalGetSelectedItem();

            switch (DataExchangeType)
            {
                case DataExchangeType.Entity:
                    r.SetPropertyValue(this.BindPropertyName, val);
                    break;

                case DataExchangeType.ID:
                    if (val == null)
                    {
                        var pType = r.GetPropertyType(this.BindPropertyName);
                        r.SetPropertyValue(this.BindPropertyName, pType.DefaultValue());
                    }
                    else
                    {
                        object key = string.IsNullOrEmpty(_key) ? val : Reflector.Bind(val).GetPropertyValue(_key);
                        r.SetPropertyValue(this.BindPropertyName, key);
                    }
                    break;
            }
        }

        public override T GetSelectedItem<T>()
        {
            this.CheckGetSetType<T>();

            if (SelectedIndex == -1)//Note:Not select anything
            {
                return default(T);
            }
            else
            {
                ComboBoxItem cbItem = SelectedItem as ComboBoxItem;
                return (T)cbItem.Item;
            }
        }

        protected override object InternalGetSelectedItem()
        {
            if (SelectedIndex == -1)//Note:Not select anything
            {
                return _boundType.DefaultValue();
            }
            else
            {
                var cbItem = this.SelectedItem as ComboBoxItem;
                return cbItem.Item;
            }
        }

        public override void SetSelectedItem<T>(T item)
        {
            this.CheckGetSetType<T>();

            InternalSetSelectedItem(item);
        }

        protected override void InternalSetSelectedItem(object item)
        {
            IReflector r = Reflector.Bind(item);
            object key = string.IsNullOrEmpty(_key) ? item : r.GetPropertyValue(_key);
            InternalSetSelectedKey(key);
        }

        protected void InternalSetSelectedKey(object key)
        {
            int i = 0;
            for (; i < this.Items.Count; i++)
            {
                var cbItem = this.Items[i] as ComboBoxItem;
                if (key.Equals(cbItem.ID))
                {
                    SelectedIndex = i;
                    return;
                }
            }
        }
    }
}
